import * as THREE from "three";
import * as d3 from "d3";
import { CSM } from 'three/examples/jsm/csm/CSM.js';
import { CSMHelper } from 'three/examples/jsm/csm/CSMHelper.js';
import gsap from "gsap";
import * as geolib from "geolib";

import type { OutlinePass } from "three/examples/jsm/Addons.js";
import jsonData from '../json/jiangsu.json'
import tag from '../textures/tag.png'

const center: any = [118.767413, 32.041544]; // 二维地图上的中心点（绘制某块地图中的建筑先确认地图中心点）
// 墨卡托投影转换
const projection = d3.geoMercator().center([104.0, 37.5]).scale(120).translate([0, 0]);
// const projection = d3.geoMercator().fitSize([width, height], jsonData);

// 地图材质颜色
// const COLOR_ARR = [0x3C6EAB, 0x2F75AC, '#0465BD', '#4350C1', '#008495']
const COLOR_ARR = [0x3C6EAB, 0x2F75AC, '#0465BD', '#357bcb', '#408db3']
// const HIGHT_COLOR = '#4fa5ff'

interface MapOption {
    camera: THREE.PerspectiveCamera,
    outlinePass: OutlinePass
}

export default function useMap(opt: MapOption) {
    const { camera, outlinePass } = opt;
    // 建一个空对象存放对象
    const mapGroup = new THREE.Group();
    jsonData.features.forEach((elem, index) => {
        // 定一个省份3D对象
        const province = new THREE.Object3D();
        // 每个的 坐标 数组
        const coordinates = elem.geometry.coordinates;
        const color = COLOR_ARR[index % COLOR_ARR.length]
        // 循环坐标数组
        coordinates.forEach(multiPolygon => {
            multiPolygon.forEach((polygon) => {
                const shape = new THREE.Shape();
                const lineMaterial = new THREE.LineBasicMaterial({ color: 0xffffff })
                const linGeometry = new THREE.BufferGeometry();
                const points = [];
                for (let i = 0; i < polygon.length; i++) {
                    let [x, y] = GPSRelativePosition(polygon[i]);

                    if (i === 0) {
                        shape.moveTo(x, -y);
                    }
                    shape.lineTo(x, -y);
                    points.push(new THREE.Vector3(x, -y, 4.01));
                }
                linGeometry.setFromPoints(points);

                const extrudeSettings = {
                    depth: 40,
                    bevelEnabled: true,
                    bevelSegments: 1,
                    bevelThickness: 0.2
                };

                const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);


                const material = new THREE.MeshStandardMaterial({
                    metalness: 1,
                    roughness: 1,
                    color: color,

                });

                const material1 = new THREE.MeshStandardMaterial({
                    metalness: 1,
                    roughness: 1,
                    color: color,

                });

                const mesh = new THREE.Mesh(geometry, [
                    material,
                    material1
                ]);
                if (index % 2 === 0) {
                    mesh.scale.set(1, 1, 1.2);
                }
 
                mesh.castShadow = true
                mesh.receiveShadow = true
                // mesh._color = color

                const line = new THREE.Line(linGeometry, lineMaterial)
                province.add(mesh);
                province.add(line);
            })
        })

        // 将geo的属性放到省份模型中
        province.name = "province";
        province.properties = elem.properties;
        const centroid = elem.properties.centroid;

        if (centroid) {
            const [x, y] = GPSRelativePosition(centroid);
            province.properties._centroid = [x, y];
        }
        mapGroup.add(province);
    })


    // 全国地图
    // mapGroup.rotation.x = -Math.PI * 0.5;
    // mapGroup.rotation.z = -Math.PI * 0.2;

    // 江苏省
    mapGroup.rotation.x = -Math.PI * 0.5;
    mapGroup.rotation.y = -Math.PI;
    mapGroup.rotation.z = -Math.PI * 1.3;
    mapGroup.scale.set(0.01, 0.01, 0.01)

    // 北京
    setTag(mapGroup, [{ value: [116.405285, 39.904989] }])
    setCsm(mapGroup, camera);
    setRaycaster(mapGroup, camera, outlinePass);

    return {
        map: mapGroup
    }
}

const setCsm = (mapGroup: THREE.Group, camera: THREE.PerspectiveCamera) => {
    let csmHelper;
    const params = {
        orthographic: false,
        fade: false,
        far: 1000,
        mode: 'practical',
        // mode: 'uniform',
        lightX: - 1,
        lightY: - 1,
        lightZ: - 1,
        margin: 100,
        lightFar: 10000,
        lightNear: 1,
        autoUpdateHelper: true,
        updateHelper: function () {
            csmHelper.update();
        }
    };
    const csm = new CSM({
        maxFar: params.far,
        cascades: 4,
        // mode: params.mode,
        parent: mapGroup,
        shadowMapSize: 1024,
        lightDirection: new THREE.Vector3(params.lightX, params.lightY, params.lightZ).normalize(),
        camera: camera
    });

    csmHelper = new CSMHelper(csm);
    csmHelper.visible = true;
    mapGroup.add(csmHelper);
}


// 绘制标注
const setTag = (mapGroup: THREE.Group, data = []) => {
    if (!data || data.length === 0) {
        return
    }
    const tagGroup = new THREE.Group();

    function paintTag(scale = 1) {
        let spriteMap = new THREE.TextureLoader().load(tag);

        data.forEach(d => {
            // 必须是不同的材质，否则鼠标移入时，修改材质会全部都修改
            let spriteMaterial = new THREE.SpriteMaterial({ map: spriteMap, color: 0xffffff });
            const { value } = d

            // 添加标点
            const sprite = new THREE.Sprite(spriteMaterial);

            if (value && value.length !== 0) {
                let [x, y] = projection(value)
                sprite.position.set(x, -y + 2, 7);
            }
            sprite.data = d
            sprite.scale.set(2 * scale, 3 * scale, 8 * scale);

            tagGroup.add(sprite)
        })
        spriteMap.dispose()
    }

    function setScale(scale = 1) {
        tagGroup.children.forEach(s => {
            s.scale.set(2 * scale, 3 * scale, 8 * scale);
        })
    }

    paintTag(0.1);

    const param = {
        value: 1.5
    }
    gsap.from(param, {
        value: 0.8,
        duration: 1,
        repeat: -1,
        yoyo: true,
        onUpdate() {
            setScale(param.value)
        }
    });

    mapGroup.add(tagGroup);
}

// 射线
const setRaycaster = (mapGroup: THREE.Group, camera: THREE.PerspectiveCamera, outlinePass: OutlinePass) => {
    const raycaster = new THREE.Raycaster();
    const mouse = new THREE.Vector2();
    const provinceInfo: HTMLElement = document.querySelector('#tag')
    const eventOffset: any = {};

    // 标注点击
    function onClick(event) {
        const container = document.querySelector('#webgl')
        // 父级并非满屏，所以需要减去父级的left 和 top
        let { top, left, width, height } = container.getBoundingClientRect()
        let clientX = event.clientX - left
        let clientY = event.clientY - top

        mouse.x = (clientX / width) * 2 - 1;
        mouse.y = -(clientY / height) * 2 + 1;

        provinceInfo.style.visibility = 'hidden';
        eventOffset.x = clientX;
        eventOffset.y = clientY;
        // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
        mouse.x = (event.clientX / window.innerWidth) * 2 - 1
        mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
        // 通过摄像机和鼠标位置更新射线
        raycaster.setFromCamera(mouse, camera)

        if (raycaster) {
            const intersects = raycaster.intersectObject(mapGroup, true);
            if (intersects.length) {
                const res = intersects.filter(res => res && res.object)[intersects.length - 1];
                if (res && res.object) {
                    outlinePass.selectedObjects = [res.object];
                    provinceInfo.style.visibility = 'visible';
                    provinceInfo.textContent = (res.object.parent as any).properties.name;
                    provinceInfo.style.left = eventOffset.x + 10 + 'px';
                    provinceInfo.style.top = eventOffset.y - 20 + 'px';
                }
            } else {
                outlinePass.selectedObjects = [];
            }
        }
    }
    document.addEventListener('click', onClick);
}

function GPSRelativePosition(objPosi) {
    const dis = geolib.getDistance(objPosi, center)
    const bearing = geolib.getRhumbLineBearing(objPosi, center)
    const x = center[0] + (dis * Math.cos(bearing * Math.PI / 180))
    const y = center[1] + (dis * Math.sin(bearing * Math.PI / 180))

    return [-x / 200, y / 200]
}